# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: jhkim

import functools
import ujson as json

from coin.base.book.types import TradeSide, create_market_trade_event
from coin.exchange.base.ws.handler import WebsocketHandler
from coin.exchange.bithumb.kr_rest.product import BithumbProduct
from coin.exchange.bithumb.book_builder import BookBuilder
from coin.exchange.bithumb.kr_rest.constants import bithumb_cointype_to_name
from coin.exchange.bithumb.ws.handler import parse_trade_side, parse_trade_side_2


@functools.lru_cache(maxsize=128)
def get_product_from_coin_type(coin_type):
  symbol = bithumb_cointype_to_name[coin_type]
  return BithumbProduct.FromStrNativeProduct(symbol)


class BithumbV2Handler(WebsocketHandler):
  def __init__(self, *, feed_checker_config=None):
    super().__init__(feed_checker_config=feed_checker_config)
    self._last_trades = {}

  def on_book(self, record, queue_data, topic_data):
    data = record.data
    if not isinstance(data, (list, dict)):
      data = json.loads(data.tobytes())

    event = data['subtype']
    assert event == 'orderbook', data
    content = data['content']
    coin_type = content['coinType']
    product = get_product_from_coin_type(coin_type)
    symbol = product.native_symbol

    if symbol not in self._books:
      self._books[symbol] = BookBuilder(symbol, book_builder_observer=self._book_builder_observer)
    book = self._books[symbol]

    book.update(content['bids'], content['asks'])
    book.update_timestamp(record.timestamp)
    book.update_topic_id(record.topic_id)
    if self._feed_checker:
      self._feed_checker.on_book(product=product, book=book, timestamp=record.timestamp)
    self.publish_event('book', symbol, book)
    return book

  def on_trade(self, record, queue_data, topic_data):
    data = record.data
    if not isinstance(data, dict):
      data = json.loads(data.tobytes())

    event = data['subtype']
    assert event == 'transaction', data
    content = data['content']
    coin_type = content['list'][0]['coinType']
    product = get_product_from_coin_type(coin_type)
    symbol = product.native_symbol
    if not self.is_subscribed('trade', symbol):
      return
    for trade_msg in content['list']:
      price = float(trade_msg['contPrice'])
      qty = float(trade_msg['contQty'])
      # buySellGb and updn are sometimes inconsistent
      if 'buySellGb' in trade_msg:
        side = parse_trade_side_2(trade_msg['buySellGb'])
      elif 'updn' in trade_msg:
        side = parse_trade_side(trade_msg['updn'])
      else:
        side = TradeSide.TRADE_NO_SIDE
      trade_event = create_market_trade_event(record.timestamp,
                                              symbol,
                                              price,
                                              qty,
                                              side,
                                              topic_id=record.topic_id)
      if self._feed_checker:
        self._feed_checker.on_trade(product=product,
                                    trade_event=trade_event,
                                    timestamp=record.timestamp)
      self.publish_event('trade', symbol, trade_event)
